package torres.entity;

import torres.ability.AbilityManager;
import torres.ability.ActionAbility;
import torres.action.Effect;
import torres.controller.Direction;
import torres.fileIO.ClassContainer;
import torres.fileIO.BasicContainer;
import torres.item.TrapItem;
import torres.map.Location;
import torres.map.Map;
import torres.map.World;
import torres.utility.Statistics;
import torres.view.ImageLoader;

/**
 * @author Torres
 * Non-player entities that occupy map with player entities. Can attack player entities. 
 */
public class NPC extends Entity {
	
	private Map belongTo;
	private World world;
	private String aiString;
	private String dialogue;
	private boolean hostile; //TODO change this to an Enum and use the state pattern
	private boolean asleep;
	
	/**
	 * Creates a new default NPC.
	 */
	public NPC() {

		super();
		name = "dragon";
		imageName = name + "E.png";
		dialogue = "Hi I am "+name;
		loader = new ImageLoader(imageName);
		image = loader.getImage();
		
		direction = Direction.EAST;
		
		currHealth = 50;
		currMana = 75;
		lives = 1;
		movement = 1;
		experience = 300;
		
		world = World.getInstance();
		belongTo = world.getCurrentMap();
	}
	
	/**
	 * Creates a new NPC with given attributes.
	 * @param name Name of image of NPC
	 * @param imageName Name of image of NPC with direction specified
	 * @param aiString String representing its AI type
	 * @param job Occupation of NPC
	 * @param intellect Intellect of NPC
	 * @param strength Strength of NPC
	 * @param agility Agility of NPC
	 * @param hardiness Hardiness of NPC
	 * @param movement Movement of NPC
	 * @param level Level of NPC
	 * @param bindWound Level of bindWound skill
	 * @param bargain Level of bargain skill
	 * @param observation Level of observation skill
	 * @param influenceRadius influenceRadius of NPC
	 * @param fireRes Fire elemental resistance of NPC
	 * @param waterRes Water elemental resistance of NPC
	 * @param airRes Air elemental resistance of NPC
	 * @param lightningRes Lightning elemental resistance of NPC
	 * @param location Location of NPC
	 * @param elementType Elemental type of the NPC
	 */
	public NPC(String name, String imageName, String aiString, Occupation job, int intellect, int strength, int agility, 
			int hardiness, int movement, int level, int bindWound, int bargain, int observation, 
			int influenceRadius,int fireRes, int waterRes, int airRes, int lightningRes, 
			Location location, Effect elementType) {
		super(name, imageName, job, intellect, strength, agility, hardiness, movement, level, bindWound, bargain,
				observation, influenceRadius, fireRes, waterRes, airRes, lightningRes, location, elementType);
		
		loader = new ImageLoader(this.getImageName());
		image = loader.getImage();
		
		direction = Direction.EAST;
		dialogue = "Hi I am "+name;
		
		currHealth = this.getMaxHealth();
		currMana = this.getMaxMana();
		lives = 1;
		movement = 1;
		
		world = World.getInstance();
		belongTo = world.getCurrentMap();
		
		this.setAIString(aiString);
	}

	/* (non-Javadoc)
	 * @see torres.entity.Entity#interact(torres.entity.Entity)
	 */
	public void interact(Entity e) {
		Avatar a = world.getPlayer();
		if(e.equals(a)&&!World.getInstance().getCurrentMap().equals(World.getInstance().getCombatArena())) {
			a.setInteractedWith(this);
			a.showActionMenu();
		}
	}
	
	/* (non-Javadoc)
	 * @see torres.entity.Entity#doMoreChecking(torres.map.Location)
	 */
	public boolean doMoreChecking(Location location) {
		if(world.isOccupied(location)) {
			
			Entity e = world.getEntity(location);
			
			if(e.equals(world.getPlayer())) {
				
				e.interact(this);
			}
		}
		
		if(belongTo.equals(world.getCurrentMap()) && world.getObstacle(location) == null && world.getTerrain(location).getLandCost() <= movement) {			
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * Sets map that NPC belongs to.
	 * @param map new NPC's map to set
	 */
	public void setBelongTo(Map map) {
		
		belongTo = map;
	}

	/**
	 * Returns the AI of NPC
	 * @return AIString of NPC
	 */
	public String getAIString() {
		return aiString;
	}
	
	/**
	 * Sets the AI of NPC
	 * @param aiString new AIString to set NPC
	 */
	private void setAIString(String aiString) {
		this.aiString = aiString;
	}
	
	/* (non-Javadoc)
	 * @see torres.entity.Entity#actionUponDeath()
	 */
	public int actionUponDeath() {
		
		World.getInstance().getPlayer().incExp(experience);
		World.getInstance().killNPC(this);
		return experience;
	}
	
	/* (non-Javadoc)
	 * @see torres.entity.Entity#save()
	 */
	@Override
	public ClassContainer save() {
		ClassContainer curClass = super.save();
		curClass.setName("NPC");
		if(aiString!=null)curClass.addProperty(new BasicContainer("aiString", aiString));
		curClass.addProperty(new BasicContainer("dialogue", dialogue));
		curClass.addProperty(new BasicContainer("hostile", hostile));
		curClass.addProperty(new BasicContainer("asleep", asleep));
		
		return curClass;
	}

	/* (non-Javadoc)
	 * @see torres.entity.Entity#load(torres.fileIO.ClassContainer)
	 */
	@Override
	public void load(ClassContainer classContainer) {
		super.load(classContainer);
		
		belongTo = World.getInstance().getCurrentMap();
		world=World.getInstance();
		BasicContainer aiStringCont = new BasicContainer("aiString", aiString);
		aiString = (String)aiStringCont.getValue();
		BasicContainer dialogueCont = new BasicContainer("dialogue", dialogue);
		dialogue = (String)dialogueCont.getValue();
		BasicContainer hostileCont = new BasicContainer("hostile", hostile);
		hostile = Boolean.parseBoolean((String)hostileCont.getValue().toString());
		BasicContainer asleepCont = new BasicContainer("aiString", asleep);
		asleep = Boolean.parseBoolean((String)asleepCont.getValue().toString());
	}
	
	/**
	 * NPC acts according to given effect. 
	 * @param effect Type of effect used on Entity
	 * @param power damage value of effect
	 * @param accuracy accuracy of given effect
	 */
	public void takeEffectHook(Effect effect, int power, int accuracy) {
		if(Statistics.attempt(this.getHardiness(), accuracy))
			switch(effect) {
				case SLEEP:  
					this.setAsleep();
					break;
				case CHARM: 
					this.setNonHostile();
					break;
				default: throw new RuntimeException("Other effects should have already been taken care of!");
			}
		else {
			this.setHostile();
		}
	}
	
	/**
	 * Returns NPC's hostility
	 * @return hostility of NPC. 
	 */
	public boolean isHostile() {
		return this.hostile;
	}
	
	/**
	 * Sets NPC to be hostile 
	 */
	public void setHostile() {
		this.hostile = true;
	}
	
	/**
	 * Sets NPC to be non-hostile
	 */
	public void setNonHostile() {
		ActionAbility a = AbilityManager.getInstance(). getActionAbility(301);
		a.useAbility(this);
		this.hostile = false;
	}
	
	public String getDialogue() {
		return dialogue;
	}
	
	public void setDialogue(String dialogue) {
		this.dialogue = dialogue;
	}

	public boolean isAsleep() {
		return this.asleep;
	}
	
	public void setAsleep() {
		this.asleep = true;
	}
	
	public void setAwake() {
		this.asleep = false;
	}
	
	/**
	 * Moves the entity in specified direction on map.
	 * @param d Direction to move.
	 * @return true if moved, else false. 
	 */
	public boolean move(Direction d) {
		String oldImageName = imageName;
		imageName = name + d.toString().charAt(0) + ".png";
		this.setDirection(d);
		
		if(imageName.compareTo(oldImageName) != 0) {
			
			loader = new ImageLoader(imageName);
			image = loader.getImage();
		}
		
		int incX = d.deltaX();
		int incY = d.deltaY();
		
		int oldX = location.getX();
		int oldY = location.getY();
			
		Location destination = Location.locationInDirection(location, d);
		if(checkLocation(destination)) /*checkLocation can change the current location */ {	
			if(location.getX() == oldX && location.getY() == oldY) {
				
				world.removeEntity(location);
				world.addEntity(this, destination);
				
				location.setX(oldX + incX);
				location.setY(oldY + incY);
			}
			if(world.hasTrap(destination)) {
				TrapItem t = world.getTrap(destination);
				t.onTouch(this);
				if(t.isConsumed())
					world.removeTrap(destination);
			}
			return true;
		}
		else {
				
			return false;
		}
	}
}
